﻿using System;
using System.Collections.Generic;
#if DEBUG
using System.Diagnostics;
#endif // #if DEBUG
using System.Net;
using System.Xml;
using System.IO;
using System.Text;

using FRL.DebtChased.Extensions;

namespace FRL.DebtChased.WorkflowObjects
{

	/// <summary>
	/// Declaration of Workflow class
	/// The workflow class
	/// </summary>
	public class Workflow
	{

		/////////////////////////////////////////////////////////////
		// Enumerations

		public enum eSummaryFormat
		{
			Audit,			// summary of workflow action for account note
			Clockwork,		// summary of workflow action for clockwork log
		}

		/////////////////////////////////////////////////////////////
		// Constants

		public const string XmlElementName = "Workflow";

		/////////////////////////////////////////////////////////////
		// Attributes

		public string m_strName;
		public string Name
		{
			get { return m_strName; }
			set { m_strName = value; }
		}

		public string m_strNotes;
		public string Notes
		{
			get { return m_strNotes; }
			set { m_strNotes = value; }
		}

		private int m_iPayDay = 1;
		public int PayDay
		{
			get { return m_iPayDay; }
			set { m_iPayDay = value; }
		}

		private int m_iDays = 12;
		public int Days
		{
			get { return m_iDays; }
			set { m_iDays = value; }
		}

		public List<WorkflowAction_Base> m_actions = new List<WorkflowAction_Base>();
		public List<WorkflowAction_Base> Actions
		{
			get { return m_actions; }
		}

		/////////////////////////////////////////////////////////////
		// Construction

		public Workflow()
		{

			// Initialize member variables

		}

		/////////////////////////////////////////////////////////////
		// Serialization

		private void _toXml(System.Xml.XmlWriter w)
		{
#if DEBUG
			_assertValidContent();
#endif // #if DEBUG

			// Write to xml writer
			w.WriteStartElement(XmlElementName);
			{

				// Write the attributes
				w.WriteAttributeString("name", m_strName);
				w.WriteAttributeString("notes", m_strNotes);
				w.WriteAttributeString("days", m_iDays.ToString());
				w.WriteAttributeString("payDay", m_iPayDay.ToString());

				// Loop through the actions
				foreach (WorkflowAction_Base action in m_actions)
					action.ToXml(w);

			}
			w.WriteEndElement();

		}

		public byte[] ToXmlBuffer()
		{
#if DEBUG
			_assertValidContent();
#endif // #if DEBUG

			// Declare return variable
			byte[] Ftmp = null;

			// Write to a memory stream
			using (MemoryStream ms = new MemoryStream())
			{

				// Declare a writer
				XmlWriter w = XmlWriter.Create(ms, Extensions.Extensions.GetXmlWriterSettings());

				// Write the document
				w.WriteStartDocument();
				{

					// Write the workflow object
					this._toXml(w);

				}
				w.WriteEndDocument();

				// Flush and close
				w.Flush();
				ms.Flush();

				// Populate a buffer
				ms.Position = 0;
				byte[] TempBuffer = ms.GetBuffer();

				// Trim the zero characters (leave one as this is unicode)
				int iLength = TempBuffer.Length;
				while (TempBuffer[iLength - 2] == 0)
					iLength--;
				Ftmp = new byte[iLength];
				Array.Copy(TempBuffer, Ftmp, iLength);

			}

			// Return variable
			return Ftmp;

		}

		public static Workflow XmlFactory(byte[] buffer)
		{

			// Declare return variable
			Workflow Ftmp = null;

			// Get the default XML workflow template
			using (MemoryStream ms = new MemoryStream(buffer, false))
			{

				// Open an xml reader
				XmlReader r = XmlReader.Create(ms, Extensions.Extensions.GetXmlReaderSettings());

				// Move to the start of the document
				while (!r.IsStartElement())
					r.Read();
				if (r.Name == XmlElementName)
				{

					// Create the workflow
					Ftmp = new Workflow();

					// Read the attributes
					Ftmp.Name = r.GetAttribute("name");
					Ftmp.Notes = r.GetAttribute("notes");
					Ftmp.m_iDays = r.SafeReadIntAttribute("days", 12);
					Ftmp.m_iPayDay = r.SafeReadIntAttribute("payDay", 1);

					// Loop through elements
					while (r.Read())
					{

						// If this is a workflow action...
						if (r.NodeType == XmlNodeType.Element)
						{

							// Construct an action
							WorkflowAction_Base newAction = WorkflowAction_Base.ConstructorFactory(r.Name);
							if (newAction != null)
							{
								if (newAction.FromXml(r))
									Ftmp.Actions.Add(newAction);
								else
								{
#if DEBUG
									Debug.Assert(false);
#endif // #if DEBUG
									Ftmp = null;
								}

							}
							else
							{
#if DEBUG
								Debug.Assert(false);
#endif // #if DEBUG
								Ftmp = null;
							}

						}

						// Break on errors
						if (Ftmp == null)
							break;

					} // while-loop

				}

			}

			// Check content
#if DEBUG
			Ftmp._assertValidContent();
#endif // #if DEBUG

			// Return variable
			return Ftmp;

		}

		/////////////////////////////////////////////////////////////
		// Operations

		public void SortActions()
		{

			// Sort actions into day and day-offset order

			// Double loop sort
			for (int i1 = 0; i1 < m_actions.Count; i1++)
			{

				// Track changes in second loop
				bool bDelta = false;
				for (int i2 = 0; i2 < m_actions.Count - 1; i2++)
				{

					// If action should proceed...
					if (
						(m_actions[i2].Day > m_actions[i2 + 1].Day)
						||
						(
							(m_actions[i2].Day == m_actions[i2 + 1].Day)
							&&
							(m_actions[i2].Offset > m_actions[i2 + 1].Offset)
						)
					)
					{

						// Swap
						WorkflowAction_Base temp = m_actions[i2 + 1];
						m_actions.RemoveAt(i2 + 1);
						m_actions.Insert(i2, temp);

						// Update flag
						bDelta = true;

					}

				}

				// If no change...
				if (!bDelta)
					break;

			}

		}

		public void GetObjectsUsed(List<int> iCommBodiesUsed, List<int> iPopupsUsed, List<int> iLetterTypesUsed)
		{
#if DEBUG
			Debug.Assert(iCommBodiesUsed.Count == 0);
			Debug.Assert(iPopupsUsed.Count == 0);
#endif // #if DEBUG

			// Loop through actions
			foreach (WorkflowAction_Base action in m_actions)
				action.GetObjectsUsed(iCommBodiesUsed, iPopupsUsed, iLetterTypesUsed);

		}

		public List<WorkflowAction_Base> GetActionsOnDay(int iDay)
		{

			// Declare return variable
			List<WorkflowAction_Base> Ftmp = new List<WorkflowAction_Base>();

			// Loop through the actions
			foreach (WorkflowAction_Base action in m_actions)
			{
				if (action.Day == iDay)
					Ftmp.Add(action);
			}

			// Return variable
			return Ftmp;

		}

		public string GetSummary( eSummaryFormat SummaryFormat )
		{

			// Build a summary string
			StringBuilder sb = new StringBuilder();

			// Loop through the actions
			foreach (WorkflowAction_Base action in this.m_actions)
			{
				if (sb.Length > 0)
					sb.Append("\n");
				sb.Append(action.GetSummary(SummaryFormat));
			}

			// Return string 
			return sb.ToString();

		}

#if DEBUG
		public void _assertValidContent()
		{

			// Check the workflow content is valid

			// Make sure all guids are valid and unique
			foreach (WorkflowAction_Base action in m_actions)
			{

				// Guid must not be null
				Debug.Assert(action.Identifier != Guid.Empty);

				// Check for duplicate
				foreach (WorkflowAction_Base action2 in m_actions)
				{

					// Ignore self
					if (action2 == action)
						continue;

					// Compare ids
					if (action.Identifier == action2.Identifier)
						Debug.Assert(false);

				}

			}

		}
#endif // #if DEBUG

	}

}
